using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Primitives;

namespace Extensions.Configuration.Database
{
    /// <summary>
    /// 默认轮询变化令牌
    /// default <see cref="IChangeToken"/> .
    /// </summary>
    public class DefaultPollingChangeToken : IPollingChangeToken
    {
        private readonly DbContextOptions<ConfigurationDatabaseContext> _dbContextOptions;

        /// <summary>
        /// 锁
        /// </summary>
        private readonly object _locker = new();

        /// <summary>
        /// 上一次扫描
        /// </summary>
        private DateTime _lastScanTime;

        private bool _changed;
        private CancellationTokenSource _tokenSource;
        private CancellationChangeToken _changeToken;

        /// <summary>
        /// 是否更改
        /// </summary>
        public bool HasChanged
        {
            get
            {
                if (this._changed)
                {
                    return this._changed;
                }

                lock (this._locker)
                {
                    this._changed = this.CalculateChanges();
                }

                return this._changed;
            }
        }

        /// <summary>
        /// 是否变化后主动回调
        /// </summary>
        public bool ActiveChangeCallbacks { get; internal set; }

        public CancellationTokenSource CancellationTokenSource
        {
            get => this._tokenSource;
            set
            {
                this._tokenSource = value;
                this._changeToken = new CancellationChangeToken(this._tokenSource.Token);
            }
        }

        public DefaultPollingChangeToken(DbContextOptions<ConfigurationDatabaseContext> dbContextOptions)
        {
            _dbContextOptions = dbContextOptions;
            _lastScanTime = DateTime.Now;
        }

        /// <summary>
        /// 注册变化回调
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        public IDisposable RegisterChangeCallback(Action<object> callback, object state)
        {
            return !this.ActiveChangeCallbacks
                ? EmptyDisposable.Instance
                : this._changeToken!.RegisterChangeCallback(callback, state);
        }

        /// <summary>
        /// 计算变化(扫描)
        /// </summary>
        /// <returns></returns>
        private bool CalculateChanges()
        {
            // 判断创建时间和更新时间是否大等于上一次扫描的时间
            // OPTIMIZE:如果创建时间或更新时间比当前时间大，那么一直会循环查库
            using var dbContext = new ConfigurationDatabaseContext(this._dbContextOptions);
            var anyAsync = dbContext.ConfigurationEntities
                .AnyAsync(entity => entity.UpdateTime >= this._lastScanTime || entity.CreateTime >= this._lastScanTime);

            Task.WhenAll(anyAsync);
            this._lastScanTime = DateTime.Now;

            return anyAsync.Result;
        }
    }
}